/* --------------------------- INCLUDES ------------------------------------- */

#include "transposition.h"
#include "substitution.h"

/* ----------------------------- CODE --------------------------------------- */

int
main(int argc, char ** argv)
{
	int ans;
	char * rta, * message;
	
	//ans = getConf(argc, argv);
	if (ans != ERROR_OK)
	{
		printf("ERROR!\n");
		return ans;
	}
	
	message = getMessage(argc, argv);
	if (message == NULL)
	{
		printf("ERROR!\n");
		return 1;
	}
	
	//rta = decrypt(argv);
	rta = decrypt(message);
	
	if (rta == NULL)
		return ERROR_GRAL;
	
	printf("Encrypted message:\n%s\n\n", message);
	printf("Decrypted message:\n%s\n\n", rta);
	
	free(rta);
	return 0;
}

/*----------------------------------------------------------------------------*/

char *
getMessage(int argc, char ** argv)
{
	char * rta = NULL;
	FILE * fp;
	long fileSize;
	int i;
	
	if (argc != 3)
	{
		fprintf(stderr, "Usage: trans [-m message | -f file]\n");
		exit(0);
	}
	
	if (strcmp(argv[1], "-m") == 0)
	{
		if (strlen(argv[2]) < 11)
		{
			fprintf(stderr, "Error: cyphered message too short\n");
			return NULL;
		}
		if((rta = malloc(strlen(argv[2]) + 1)) == NULL) 
		{
			fprintf(stderr, "Error: can't allocate space");
			return NULL;
		}
		strcpy(rta, argv[2]);
	}
	else if (strcmp(argv[1], "-f") == 0)
	{
		if((fp = fopen(argv[2], "r"))==NULL)
		{
			printf("Cannot open file.\n");
			exit(0);
		}
		fseek(fp, 0, SEEK_END);
		fileSize = ftell(fp);
		rewind(fp);
		
		if (fileSize < 1)
		{
			fprintf(stderr, "File has no data\n");
			exit(1);
		}
		
		if ((rta = malloc(fileSize+2)) == NULL)
		{
			fprintf(stderr, "Unable to allocate %ld bytes of memory\n", fileSize);
			exit(1);
		}
		
		for (i=0; i<fileSize; i++)
		{
			rta[i] = fgetc(fp);
			if(rta[i] == -47)
			{
				rta[i++] = -61;
				rta[i] = -111;
			}
		}
		rta[i] = '\0';
		fclose(fp);
	}
	
	printf("TEXT FROM FILE: %s\n\n", rta);
	return rta;
}

/*----------------------------------------------------------------------------*/

static int
getConf(int argc, char ** argv)
{
	if( argc != 2 || strlen(argv[1]) < 11 )
		return ERROR_PARAM;
	else
		return ERROR_OK;
}

/*----------------------------------------------------------------------------*/

static char *
decrypt(char * message)
{
	int i, len, ans;
	int key[11];
	char * rta;
	
	/* Utilizo el ataque de indice de coincidencias */
	len = coincidenceIndex(message);
	
	/* Busco la clave para desencriptar */
	findkey(message, len, key);
	
	if ((rta = malloc(strlen(message)*2 + 1)) == NULL)
		return NULL;
	
	/* Desencripto */
	if (rotarV(message, key, len, rta) != ERROR_OK)
		return NULL;
	
	ans = checkMethod(message, rta, key, len);
	
	switch (ans)
	{
		case CESAR:
			printf("The message was encrypted using CESAR.\n"
				   "The used key was: \"");
			printKey(key[0]);
			printf("\"\n\n");
			break;
		
		case VIGENERE:
			printf("The message was encrypted using VIGENERE.\n"
				   "The used key was: \"");
			for( i=0; i < len; i++ )
				printKey(key[i]);
			printf("\"\n\n");
			break;
			
		case TRANS:
			printf("The message was encrypted using COLUMN TRANSPOSITION.\n");
			decryptTransposition(message, rta);
	}
	
	return rta;
}

/*----------------------------------------------------------------------------*/

static int
checkMethod(char * texto, char * rta, int * key, int len)
{
	int i;
	
	for( i=0; i < len-1; i++ )
	{
		if( key[i] != key[i+1] )
			return VIGENERE;
	}
	if( key[0] )
		return CESAR;
	return TRANS;
}

/*----------------------------------------------------------------------------*/

static void
printKey(int key)
{
	if( 27-key < 14)
		printf("%c", 'A'+(27-key));
	else if( key == 0 )
		printf("A");
	else if( 27-key == 14 )
		printf("Ã‘");
	else
		printf("%c", 'A'+(27-key)-1);
	return;
}

/*----------------------------------------------------------------------------*/

static int 
coincidenceIndex(char * message)
{
	int rta, i, len;
	float ci, aux;
	char * copy;
	
	/* Hago una copia del original, que voy a rotar (horizontalmente) */
	if( (copy= malloc((len= strlen(message)) + 1)) == NULL )
		return ERROR_MEM;
	strcpy(copy, message);
	
	for( i=0; i < MAX_KEY_LEN; i++ )
	{
		rotarH(copy);
		ci= calcularCI(message, copy);
		if( aux < ci )
		{
			aux= ci;
			rta= i;
		}
	}
	return rta+1;
}

/*----------------------------------------------------------------------------*/

static int
findkey(char * message, int len, int * key)
{
	int length, i, j, k;
	int frecs[10], letras[27]= {0};
	float ans, tablaf[27], aux[27]= {0.0};
	/* Frecuencias del alfabeto castellano */
	float fr[27]= {12.53, 1.42, 4.68, 5.86, 13.68, 0.69, 1.01, 0.70, 6.25, 0.44, 
		0.00, 4.97, 3.15, 6.71, 0.03, 8.68, 2.51, 0.88, 6.87, 7.98, 4.63, 3.93,
	0.90, 0.02, 0.22, 0.90, 0.52};
	
	for( i=0; i < 10; i++ )
		frecs[i]= -1;
	length= strlen(message);
	
	/* Busco la letra mas frecuente de cada grupo */
	for( i=0; i < len; i++ )
	{
		ans= 0;
		frecuencias(message+i, letras, len);
		
		for( j=0; j < 27; j++ )
		{
			tablaf[j]= letras[j]/(float)length;
			aux[j]=0.0;
		}
		for( j=0; j<27; j++ )
		{
			/* Hago producto escalar */
			for( k=0; k < 27; k++)
				aux[j]+= tablaf[k]*fr[k];
			if( ans < aux[j] )
			{
				ans= aux[j];
				key[i]= j;
			}
			/* Roto el vector de frecuencias */
			nextFr(fr);
		}
	}
	return 0;
}

/*----------------------------------------------------------------------------*/

static void
nextFr(float * fr)
{
	int i;
	float aux;
	
	aux= fr[0];
	for( i=0; i < 26; i++ )
		fr[i]= fr[i+1];
	fr[i]= aux;
	return;
}

/*----------------------------------------------------------------------------*/

static int
rotarV(char * texto, int * key, int length, char * rta)
{
	int i, j, k, token, len;
	char * aux, * cpy;
	
	len= strlen(texto);
	if( (aux= malloc(len*2 + 1)) == NULL )
		return ERROR_MEM;
	
	/* Hago una copia del original, reemplazando las enies por '.' */
	for( i=0, j=0; j < len; i++, j++)
	{
		if(texto[j] > 0)
			aux[i]= texto[j];
		else
		{
			aux[i]= '.';
			j++;
		}
	}
	aux[i]= '\0';
	
	cpy= texto, texto= aux,	aux= cpy;
	for( i= 0; i < length; i++ )
	{
		if( !key[i] )
		{
			for( k= 0; k < len; k+= length)
				rta[i+k]= texto[i+k];
			continue;
		}
		if( key[i] <= 14 )
			token= 'o' - key[i];
		else
			token= 'z' - (key[i]-15);
		
		for( j= 0; j < len; j+= length )
		{
			/* Si es enie */
			if( *(texto + i + j) == '.' )
			{
				if( key[i] >= 1 && key[i] <= 12 )
				{
					if( *(texto + i + j+1) == -79 )
						rta[i+j]= 'n' + key[i];
					else
						rta[i+j]= 'N' + key[i];
				}
				else
				{
					if( *(texto + i + j+1) == -79 )
						rta[i+j]= 'a' + key[i]-13;
					else
						rta[i+j]= 'A' + key[i]-13;
				}
			}
			/* Si es la letra q se reemplazara por enie */
			else if( *(texto + i + j) == token || 
					*(texto + i + j) == toupper(token) )
				rta[i+j]= '.';
			
			/* Si no da la vuelta */
			else if( *(texto + i + j) >= 'a' && *(texto + i+j) <= 'z'-key[i] ||
					*(texto + i + j) >= 'A' && *(texto + i + j) <= 'Z'-key[i] || 
					(*(texto + i + j) == 'z'-key[i]+1 || 
					 *(texto + i + j) == 'Z'-key[i]+1) && key[i] >= 12 && 
					*(texto + i + j) != 'o' && *(texto + i + j) != 'O' )
			{
				rta[i+j]= *(texto + i + j)+key[i];
				if( *(texto + i + j) < 'o' && *(texto + i + j)+key[i] >= 'o' ||
				   *(texto + i + j) < 'O' && *(texto + i + j)+key[i] >= 'O' )
					rta[i+j]--;
			}
			/* Si da la vuelta */
			else if( *(texto + i + j) > 'z'-key[i] && *(texto + i + j) <= 'z' ||
					*(texto + i + j) > 'Z'-key[i] && *(texto + i + j) <= 'Z')
			{
				rta[i+j]= *(texto + i + j)+key[i] - ('z'-'a') - 1;
				if( (*(texto + i + j) >= 'a' && *(texto + i + j) < 'o' || 
					 (*(texto + i + j) >= 'A' && *(texto + i + j) < 'O') && 
					 *(texto + i + j)+key[i] - ('z'-'a') - 1 < *(texto + i + j)) ||
				   *(texto + i + j)+key[i] - ('z'-'a') - 1 >= 'o' || 
				   *(texto + i + j)+key[i] - ('z'-'a') - 1 >= 'O' && 
				   isupper(*(texto + i + j)+key[i] - ('z'-'a') - 1) )
					rta[i+j]--;
			}
			/* Si no es una letra */
			else
				rta[i+j]= *(texto + i + j);
		}
	}
	rta[len]= '\0';
	
	strcpy(texto, rta);
	for( i=0, j=0; texto[i] != '\0'; i++, j++ )
	{
		if( texto[i] == '.' )
		{
			rta[j]= -61;
			rta[++j]= -111;
		}
		else
			rta[j]= texto[i];
	}
	free(texto);
	return ERROR_OK;
}

/*----------------------------------------------------------------------------*/

static int
rotarH(char * text)
{
	int i, len, aux;
	
	/*ToDo: Arreglar: si el texto empieza con ascii extendido, pierdo 1 letra */
	len= strlen(text);
	aux= *text;
	for( i=0; i < len-1 ; i++ )
		text[i]= text[i+1];
	text[i]= aux;
	
	return 0;
}

/*----------------------------------------------------------------------------*/

static float
calcularCI(char * texto1, char * texto2)
{
	int i, len, count= 0;
	
	len= strlen(texto1);
	for( i=0; i < len; i++ )
	{
		if( tolower(texto1[i]) == tolower(texto2[i]) && texto1[i] != '.' && 
		   texto1[i] != ',' &&	texto1[i] != ' ' )
			count++;
	}
	return count/(float)len;
}

/*----------------------------------------------------------------------------*/

static void
frecuencias(char * argv, int * letras, int step)
{
	int i, cant= 0;
	char * limit;
	
	/* Inicializo vector de letras en 0 */
	for( i=0; i < 27; i++ )
		letras[i]= 0;
	
	limit= argv+strlen(argv);
	while( argv < limit )
	{
		if( *argv == -61 && (*(argv+1) == -79 || *(argv+1) == -111) )
		{
			letras[14]++;
			argv++;
			cant++;
		}
		else if( isalpha(*argv) )
		{
			if( tolower(*argv) < 'o' )
				letras[tolower(*argv)-'a']++;
			else
				letras[tolower(*argv)-'a'+1]++;
			cant++;
		}
		else if( *argv < 0 )
			argv++;
		if( step == 1 )
			argv++;
		else
		{
			for( i=0; i < step; i++ )
			{
				if( *argv == -61 && (*(argv+1) == -79 || *(argv+1) == -111) )
					argv++;
				argv++;
			}
		}
	}
	return;
}

